로딩 중이에요... 🐣
20 템플릿, 미들웨어 | ✅ 저자: 이유정(박사)
Jinja(진자)는 파이썬에서 사용하는 HTML 템플릿 엔진입니다.
- HTML 안에
{% ... %}
,{{ ... }}
같은 파이썬 코드나 변수를 넣어서 웹페이지를 자동으로 만들어주는 도구예요. - 예: 장고(Django), 플라스크(Flask), FastAPI에서 HTML을 자동 생성할 때 사용됩니다. 쉽게 설명하면 Jinja = HTML 코드 속에 파이썬 변수를 껴 넣어서 웹페이지를 만들어주는 도구입니다.
Jinja2는 Django 템플릿과 거의 똑같은 역할을 하는 템플릿 엔진입니다.
항목 | Jinja2 (FastAPI, Flask 등) | Django 템플릿 |
---|---|---|
사용 목적 | HTML + 변수/제어문 섞어서 웹페이지 생성 | 동일 |
문법 | {{ 변수 }} , {% for %} , {% if %} |
거의 동일 |
장점 | 가볍고 빠름, 다양한 프레임워크에 사용됨 | Django에 최적화됨 |
내장 함수 | range() , len() , default() 등 다양 |
비슷하지만 Django는 자체 필터 많음 |
사용자 정의 필터 | @app.template_filter 등으로 직접 추가 가능 |
Django도 custom filter 가능 |
일반 HTML
<h1>안녕하세요!</h1>
이건 항상 "안녕하세요!"만 출력돼요. 사용자 이름을 바꿀 수 없죠.
Jinja 템플릿 (파이썬 변수 삽입)
<h1>안녕하세요, {{ username }}님!</h1>
그리고 Python에서 이렇게 보내줍니다.
from fastapi import FastAPI, Request
from fastapi.templating import Jinja2Templates
app = FastAPI()
templates = Jinja2Templates(directory="templates")
@app.get("/hello")
def say_hello(request: Request):
return templates.TemplateResponse("hello.html", {
"request": request,
"username": "Eunice"
})
이 코드를 실행하면 "안녕하세요, Eunice님!"** 이라고 실제로 HTML이 바뀌어 렌더링돼요.
Jinja2설치
pip install jinja2
폴더구조
jinja_app/
│
├── main.py
└── templates/
├── hello.html
└── users.html
main.py
from fastapi import FastAPI, Request
from fastapi.responses import HTMLResponse
from fastapi.templating import Jinja2Templates
app = FastAPI()
# 템플릿 디렉토리 설정
templates = Jinja2Templates(directory="templates")
# 기본 인사 페이지
@app.get("/", response_class=HTMLResponse)
def read_root(request: Request):
return templates.TemplateResponse("hello.html", {
"request": request,
"username": "유정이"
})
# 사용자 리스트 페이지
@app.get("/users", response_class=HTMLResponse)
def show_users(request: Request):
user_list = ["Alice", "Bob", "Charlie", "David"]
return templates.TemplateResponse("users.html", {
"request": request,
"users": user_list
})
templates/hello.html
<!DOCTYPE html>
<html>
<head>
<title>Hello</title>
</head>
<body>
<h1><a href=/users>안녕하세요, {{ username }}님!</a></h1>
</body>
</html>
fast api에서 링크이동은 라우트 경로로 이동합니다.
templates/users.html
<!DOCTYPE html>
<html>
<head>
<title>사용자 목록</title>
</head>
<body>
<h2>사용자 리스트</h2>
<ul>
{% for user in users %}
<li>{{ user }}</li>
{% endfor %}
</ul>
</body>
</html>
실행 프로젝트 폴더로 이동
cd jinja_app
필요한 패키지 설치 uvicorn[standard]
pip install fastapi jinja2 "uvicorn[standard]"
FastAPI는 자체 웹서버가 없기 때문에, 서버 실행을 위해 uvicorn이라는 ASGI 서버가 필요합니다.
서버 실행
uvicorn main:app --reload
- 브라우저로 접속
- http://localhost:8000/ → "안녕하세요, 유정이님!"
- http://localhost:8000/users → 사용자 리스트 출력
fast api하드코딩 방식보다 Jinja2의 url_for
사용을 권장합니다:
<a href="{{ url_for('show_users') }}">안녕하세요, {{ username }}님!</a>
단, 이 경우에는 show_users()
함수에 이름을 붙여야 합니다:
@app.get("/users", response_class=HTMLResponse, name="show_users")
def show_users(request: Request):
미들웨어(Middleware)란?
미들웨어는 “중간에서 도와주는 프로그램”이에요.
쉽게 말해서, 두 프로그램 사이에서 '다리' 역할을 하는 도우미입니다.
미들웨어는 운영체제(OS)가 제공하는 기본 기능 외에,
애플리케이션들이 서로 소통하고 동작할 수 있게 해주는 추가 소프트웨어입니다.
미들웨어가 하는 일 예시:
기능 | 예시 |
---|---|
보안 | 요청에 인증 정보가 있는지 확인 |
로깅 | 누가 언제 어떤 요청을 보냈는지 기록 |
오류 처리 | 에러가 나면 예쁘게 알려주기 |
CORS | 다른 출처에서 온 요청을 허용할지 판단 |
데이터 압축 | 응답 데이터를 gzip으로 압축해서 보내기 |
Django, FastAPI, Express.js 등에서는 요청(Request)이 들어오고 → 응답(Response)을 보내기 전에 중간에서 무언가를 처리하는 코드를 미들웨어라고 부릅니다. |
요청이 처리되기까지 걸린 시간(초)을 응답 헤더에 X-Process-Time
이라는 이름으로 붙여서 반환하는 예시코드:
import time
from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse
app = FastAPI()
# 미들웨어 구현
@app.middleware("http")
async def add_process_time_header(request: Request, call_next):
start_time = time.time() # 요청 시작 시간 기록
response = await call_next(request) # 실제 요청 처리
process_time = time.time() - start_time # 처리 시간 계산
response.headers["X-Process-Time"] = str(process_time) # 응답 헤더에 추가
return response
# 테스트용 엔드포인트
@app.get("/ping")
async def ping():
return {"message": "pong"}
실행해보기
uvicorn main:app --reload
결과 확인 방법:
브라우저에서 http://localhost:8000/ping
접속
F12 → Network 탭 → ping 요청 클릭
"Headers" 탭 → Response Headers에서 아래 항목 확인:
X-Process-Time: 0.0003476142883300781
이게 바로 미들웨어가 처리한 요청 처리 시간이에요.
터미널에서
curl -i http://localhost:8000/ping
결과:
HTTP/1.1 200 OK
X-Process-Time: 0.00045
content-type: application/json
...
{"message":"pong"}
X-Process-Time
헤더가 응답에 붙어 있으면 미들웨어가 잘 작동한 것입니다!
실제 FastAPI 프로젝트구조에서 미들웨어 코드 위치:
myproject/
├── app/
│ ├── __init__.py
│ ├── main.py ← FastAPI 인스턴스(app) 생성 위치
│ ├── middleware/ ← ✅ 미들웨어 모듈 저장
│ │ ├── __init__.py
│ │ └── process_time.py ← 지금 만든 미들웨어
│ ├── routes/
│ │ └── ping.py ← /ping 같은 라우트들
│ └── ...
├── requirements.txt
└── run.py ← uvicorn 실행 진입점
미들웨어 코드 분리 예시 (process_time.py
)
# app/middleware/process_time.py
import time
from fastapi import Request
async def add_process_time_header(request: Request, call_next):
start_time = time.time() # 요청 시작 시각 기록
response = await call_next(request) # 실제 라우터 핸들러 호출
process_time = time.time() - start_time # 처리 시간 계산
response.headers["X-Process-Time"] = str(process_time)
# 응답 헤더에 삽입
return response
목적:
요청이 들어온 시점부터 응답을 마치기까지 걸린 시간을 계산해서,
응답 헤더에 X-Process-Time
이라는 커스텀 정보를 추가하는 것이 목적입니다.
목적 | 예시 |
---|---|
요청 시간 측정 | API 응답 지연 여부 모니터링 |
요청 로깅 | 누가 어떤 URL에 요청을 했는지 로그 파일에 저장 |
인증 검사 | Authorization 헤더 유효성 확인 후 401 반환 |
응답 가공 | 특정 응답에 공통 헤더 삽입 (예: CORS, 보안 헤더 등) |
사용자 IP 추적 | request.client.host 로 사용자 IP 기록 |
FastAPI 인스턴스(app)에 미들웨어 등록 (main.py
)
# app/main.py
from fastapi import FastAPI
from app.middleware.process_time import add_process_time_header
app = FastAPI()
# 미들웨어 등록
app.middleware("http")(add_process_time_header)
# 라우터 등록 예시
from app.routes import ping
app.include_router(ping.router)
라우터 예시 (routes/ping.py
)
# app/routes/ping.py
from fastapi import APIRouter
router = APIRouter()
@router.get("/")
async def root():
return {"message": "Welcome to the API"}
@router.get("/ping")
async def ping():
return {"message": "pong"}
main.py에 있던 기능을 routes/ping.py
로 분리해서 라우터로 등록한 것
일반적인 fastAPI 라우터 구조예시
app/
├── main.py ← 앱 생성 및 라우터 등록
├── routes/
│ ├── auth.py ← 로그인, 회원가입, 인증
│ ├── users.py ← 회원정보, 유저조회
│ ├── posts.py ← 글쓰기, 조회, 삭제
│ ├── comments.py ← 댓글 작성/삭제
│ ├── ping.py ← 서버 상태 점검
│ └── __init__.py
실행 진입점 (run.py
)
# run.py
import uvicorn
if __name__ == "__main__":
uvicorn.run("main:app", reload=True)
서버실행
python run.py
결과:
위 결과의 전체 요약
- 요청 URL:
http://127.0.0.1:8000/
→ 브라우저가 FastAPI 서버에 GET 요청 보냄 - 상태 코드:
200 OK
→ 요청 성공 - 응답 헤더: 서버가 보낸 정보 (
Content-Type
,X-Process-Time
등) - 요청 헤더: 클라이언트(브라우저)가 보낸 정보 (
Accept
,Cookie
,User-Agent
등)
1. 일반 정보 (요청 정보)
항목 | 의미 |
---|---|
요청 URL | http://127.0.0.1:8000/ — 루트("/") 경로 요청 |
요청 메서드 | GET — 데이터 조회용 요청 |
상태 코드 | 200 OK — 정상 응답 |
원격 주소 | 127.0.0.1:8000 — 로컬 서버 |
리퍼러 정책 | strict-origin-when-cross-origin — 크로스 도메인 요청 시 리퍼러 제어 정책 |
2. 응답 헤더
키 | 값 | 설명 |
---|---|---|
Content-Length |
32 | 응답 데이터 바이트 수 |
Content-Type |
application/json |
응답 데이터가 JSON 형식임 |
Date |
Wed, 23 Jul 2025 19:19:08 GMT |
응답한 시간 (UTC) |
Server |
uvicorn |
FastAPI 서버 백엔드가 uvicorn 임 |
X-Process-Time |
0.0003... |
요청 처리에 걸린 시간 (초, 미들웨어에서 설정됨) |
3. 요청 헤더
키 | 값 | 설명 |
---|---|---|
Accept |
text/html,... |
브라우저가 수용 가능한 응답 타입들 |
Accept-Encoding |
gzip, deflate, br, zstd |
압축 방식 |
Accept-Language |
ko-KR,... |
수용 가능한 언어 |
Cache-Control |
max-age=0 |
캐시 사용 안 함 (최신 요청 원함) |
Connection |
keep-alive |
연결 유지 |
Cookie |
다양한 세션 정보 포함 <br>csrftoken , sessionid , _xsrf 등 → 대부분 로그인, 인증 관련 정보 |
|
Host |
127.0.0.1:8000 |
요청 보낸 서버 주소 |
User-Agent |
Mozilla/5.0... |
브라우저 정보 (Chrome, Windows) |
주요 포인트
- FastAPI 서버 정상 작동 중 (
200 OK
,application/json
,uvicorn
) X-Process-Time
헤더는 미들웨어가 응답 시간 측정에 성공했다는 의미- 요청 헤더에는 브라우저 기본 정보와 함께
cookie
에 세션/CSRF 토큰이 자동 포함됨 - 개발용 환경이기 때문에
127.0.0.1
에서 테스트 중
헤더는 서버와 클라이언트 간의 약속입니다. 헤더는 택배 상자의 운송장이라고 앞전에 언급했습니다.
- 택배 상자 안에는 우리가 보내고 싶은 데이터(body) 가 들어 있고,
- 바깥에는 운송장(header) 이 붙어 있어요.
- 어디로 보내는지 (Host)
- 누가 보냈는지 (Authorization)
- 어떤 포장인지 (Content-Type)
- 뭘로 받고 싶은지 (Accept)
즉, 서버가 그 요청을 올바르게 이해하고 처리하려면,
운송장(헤더)을 꼭 붙여야 하는 거예요!
비유하자면 클라이언트(사용자)는 택배를 보내는 사람이고, 헤더는 운송장이며 미들웨어는 택배 창구 직원입니다.
사용자가 택배를 보내면, 창구 직원(미들웨어)이 운송장(헤더)을 보고:
- 주소가 잘 되었는지 확인하고
- 무게를 재서 처리 시간(
X-Process-Time
)을 붙이고 - 보안 검사도 하고
- 스티커(응답 헤더)를 하나 더 붙여서 서버에 전달하는 것!
우리가 연습한 코드는 FastAPI의 미들웨어 기능중 일부이며 응답 헤더에 처리 시간(X-Process-Time
)을 기록하는 미들웨어를 테스트해본 것입니다.
그외에도 이런것들을 사용할수 있어요:
요청 전 검사
: IP 제한, 토큰 확인, 요청 헤더 검사 등요청 처리 시간 측정
: time.time()으로 경과 시간 계산응답 헤더 추가
: X-Process-Time, X-API-Version 등 삽입로그 출력
: 요청/응답 정보를 로그로 기록예외 처리
: 에러 응답 포맷 지정CORS, GZip, 세션
: 기본 내장 미들웨어 (자동 등록 또는 설정 필요)
헤더 정보에 추가할 수 있는 주요 항목:
인증 관련
Authorization
: 인증 토큰 (예:Bearer <token>
,Basic ...
)Cookie
: 세션 정보 포함X-CSRFToken
: CSRF 보안 토큰 (주로 POST/PUT 등에서)
데이터 형식 관련
Content-Type
: 요청 본문의 타입 (예:application/json
,multipart/form-data
)Accept
: 원하는 응답 타입 (예:application/json
,text/html
)
보안 및 출처 확인
Origin
: 요청이 발생한 출처 (CORS 정책에 사용)Referer
: 사용자가 방금 전에 있던 페이지 주소X-Requested-With
: Ajax 요청 여부 확인 (예:XMLHttpRequest
)
클라이언트 정보
User-Agent
: 브라우저/앱 종류 및 버전Host
: 요청 대상 서버 호스트 (예:example.com
)
커스텀 헤더 (개발자 정의 가능)
X-API-Version
: API 버전 명시X-App-Name
: 앱 이름 또는 클라이언트 종류 식별- 기타
X-
접두사로 시작하는 자유 정의 헤더 가능